package com.fanghuaiming.dtx.aboc.service.impl;

import com.fanghuaiming.dtx.aboc.dao.mapper.AbocAccountInfoMapper;
import com.fanghuaiming.dtx.aboc.dao.model.AbocAccountInfo;
import com.fanghuaiming.dtx.aboc.service.DtxAbocService;
import com.fanghuaiming.dtx.aboc.spi.icbc.DtxIcbcFeignClient;
import com.fanghuaiming.dtx.common.model.icbc.IcbcOrderInfo;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hmily.annotation.Hmily;
import org.dromara.hmily.annotation.PatternEnum;
import org.dromara.hmily.common.exception.HmilyRuntimeException;
import org.dromara.hmily.core.concurrent.threadlocal.HmilyTransactionContextLocal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/****
 * @description: ABOC服务接口实现类
 * @version:1.0.0
 * @author fanghuaiming
 * @data Created in 2020/11/27 下午1:59
 *
 */
@Service
@Slf4j
public class DtxAbocServiceImpl implements DtxAbocService {

    /**
     * ABOC的mapper
     */
    @Autowired
    private AbocAccountInfoMapper abocAccountInfoMapper;

    /**
     * ICBC微服务调用
     */
    @SuppressWarnings("all")
    @Autowired
    private DtxIcbcFeignClient dtxIcbcFeignClient;

    /**
     * @Description:  TCC-第一阶段Try
     * ABOC账户扣款业务:
     *              1、try幂等校验
     *              2、try悬挂处理
     *              3、检查余额是否够扣款金额
     *              4、扣减指定金额
     *
     *
     * @param:
     * @return:
     * @auther: fanghuaiming
     * @date: 2020/12/2 下午10:30
     * @version:1.0.0
     */
    @Override
    @Hmily(confirmMethod = "commitAbocAccount",cancelMethod = "rollbackAbocAccount",pattern = PatternEnum.TCC)
    public void updateAbocAccountBalance(AbocAccountInfo abocAccountInfo) {
        //获取全局事务id
        String transactionId = HmilyTransactionContextLocal.getInstance().get().getTransId();
        log.info("========== TCC-第一阶段Try=执行【updateAbocAccountBalance】进行ABOC账户扣款,全局事务xid:{} ==========",transactionId);

        //Try进行幂等校验,如果处理过就不再处理,不然会多扣钱了
        log.info("========== TCC-第一阶段 try幂等校验 全局事务xid:{},请求ABOC账户:{}扣款金额:{} ==========",transactionId,abocAccountInfo.getAccountName(),abocAccountInfo.getAccountBalance());
        if(abocAccountInfoMapper.isExistTry(transactionId) > 0){
            //代表已经执行过try
            log.info("========== TCC-第一阶段 try已被执行过 全局事务xid:{},请勿重复执行 ==========",transactionId);
            return ;
        }

        //Try悬挂处理,如果cancel、confirm中有一个已经执行了,try就不再执行,不然就会多扣钱了
        if(abocAccountInfoMapper.isExistConfirm(transactionId) > 0
            || abocAccountInfoMapper.isExistCancel(transactionId) >0){
            log.info("========== TCC-第一阶段 try悬挂处理 --> confirm/cancel已经执行,不允许执行try 全局事务xid:{} ==========",transactionId);
            return ;
        }

        //扣减金额
        if(abocAccountInfoMapper.subtractAbocAccountBalance(abocAccountInfo.getAccountNo(),abocAccountInfo.getAccountBalance()) <= 0){
            //Exception和RuntimeException可以被自动捕获,Hmily强调不允许捕获异常应该抛出由Hmily处理
            throw new HmilyRuntimeException("updateAbocAccountBalance扣减ABOC金额失败,余额不足 全局事务xid:" + transactionId);
        }
        //插入try执行记录,用于幂等判断
        abocAccountInfoMapper.addTry(transactionId);

        //远程调用李四进行转账
        if(!dtxIcbcFeignClient.transferIcbcAccount(
                IcbcOrderInfo
                        .builder()
                        .accountBalance(abocAccountInfo.getAccountBalance())
                        .accountNo(abocAccountInfo.getAccountNo())
                        .build())){
            throw new HmilyRuntimeException("transferIcbcAccount 远程调用转账ICBC账户微服务失败 全局事务xid:" + transactionId);
        }

        //本地事务扣减ABOC账户成功,远程调用ICBC转账成功,制造异常
        if(abocAccountInfo.getAccountBalance() == 500){
            throw new HmilyRuntimeException("transferIcbcAccount 本地事务扣减ABOC账户成功,远程调用ICBC转账成功,制造异常 全局事务xid:" + transactionId);
        }

        log.info("==========扣款完成!!! TCC-第一阶段Try=执行【updateAbocAccountBalance】进行ABOC账户,全局事务xid:{} ==========",transactionId);
    }

    /**
     * @Description: TCC-第二阶段Confirm
     *
     * 空回滚:什么也不做
     *
     * @param: 参数要和try一致
     * @return: 返回值要和try一致
     * @auther: fanghuaiming
     * @date: 2020/12/2 下午10:37
     * @version:1.0.0
     */
    @SuppressWarnings("all")
    @Transactional
    public void commitAbocAccount(AbocAccountInfo abocAccountInfo){
        //获取全局事务id
        String transactionId = HmilyTransactionContextLocal.getInstance().get().getTransId();
        log.info("========== TCC-第二阶段Confirm confirm空回滚处理【commitAbocAccount】,全局事务xid:{} ==========",transactionId);
    }

    /**
     * @Description: TCC-第二阶段Cancel
     *
     *          cancel幂等校验
     *          cancel空回滚处理
     *          增加指定金额
     *
     * @param: 参数要和try一致
     * @return: 返回值要和try一致
     * @auther: fanghuaiming
     * @date: 2020/12/2 下午10:38
     * @version:1.0.0
     */
    @SuppressWarnings("all")
    @Transactional
    public void rollbackAbocAccount(AbocAccountInfo abocAccountInfo){
        //获取全局事务id
        String transactionId = HmilyTransactionContextLocal.getInstance().get().getTransId();
        log.info("========== TCC-第二阶段Cancel=执行【rollbackAbocAccount】进行增加ABOC指定金额,全局事务xid:{} ==========",transactionId);

        //cancel进行幂等校验,如果处理过就不再处理,不然会多增加钱了
        log.info("========== TCC-第二阶段 cancel幂等校验 全局事务xid:{},请求ABOC账户:{}增加金额:{} ==========",transactionId,abocAccountInfo.getAccountName(),abocAccountInfo.getAccountBalance());
        if(abocAccountInfoMapper.isExistCancel(transactionId) > 0){
            //代表已经执行过cancel
            log.info("========== TCC-第二阶段 cancel已被执行过 全局事务xid:{},请勿重复执行 ==========",transactionId);
            return ;
        }

        //cancel空回滚处理,如果try扣过钱没有执行,cancel是不允许执行的,不然就会给账户多增加钱了
        if(abocAccountInfoMapper.isExistTry(transactionId) <=0){
            log.info("========== TCC-第二阶段 cancel空回滚处理 try没有执行,不允许执行cancel回滚 全局事务xid:{} ==========",transactionId);
            return ;
        }

        //增加指定金额
        abocAccountInfoMapper.addAbocAccountBalance(abocAccountInfo.getAccountNo(),abocAccountInfo.getAccountBalance());
        //插入一条cancel的执行记录,幂等判断

        log.info("========== TCC-第二阶段Cancel=执行【rollbackAbocAccount】进行增加ABOC指定金额成功!!!,全局事务xid:{} ==========",transactionId);
    }
}
